# include "project.h" // ja inclui o types e logsystem


static int actualProjectSaved = 0;
static Project *actualProject = NULL;

char * getProjectName () {
	if (!actualProject) return NULL;
	return actualProject->name;
}
ArrayList * getStudentsList () {
	if (!actualProject) return NULL;
	else return actualProject->students;
}

ChainingHashMap * getUCEsMap () {
	if (!actualProject) return NULL;
	else return actualProject->ucesMap;
}

void initializeProjectSystem () {
	actualProjectSaved = 0;
	actualProject = NULL;
}
static int projectExists (const char *name) {
	char *path = malloc(sizeof(char)*(strlen(PROJECT_PATH)+strlen(name)+4));
	sprintf(path,"%s/%s",PROJECT_PATH,name);
	FILE *fp;
	fp = fopen(path,"r");
	if (fp) {
		fclose(fp);
		free(path);
		return 1;
	} else {
		free(path);
		return 0;
	}
}
static int createStoreProjectsDir () {
	FILE *fp;
	char *path = (char *) malloc(sizeof(strlen(PROJECT_PATH)+4));
	sprintf(path,"%s",PROJECT_PATH);
	fp = fopen(path,"r");
	if (fp) {
		fclose(fp);
		return 1;
	} else {
		mkdir(path,S_IRWXU | S_IRWXG | S_IRWXO);
		return 1;
	}
}
static int createProjectDir (const char *name) {
	FILE *fp;
        char *path = (char *) malloc(sizeof(strlen(PROJECT_PATH)+strlen(name)+5));
        sprintf(path,"%s/%s",PROJECT_PATH,name);
        fp = fopen(path,"r");
        if (fp) {
                fclose(fp);
                return 1;
        } else {
                mkdir(path,S_IRWXU | S_IRWXG | S_IRWXO);
                return 1;
        }

}
static LinkedList * listProjects () {
	LinkedList *list = NULL;
	char *path = malloc(sizeof(char)*(strlen(PROJECT_PATH)+4));
	sprintf(path,"%s",PROJECT_PATH);
	DIR *projectDir = opendir(path);
	struct dirent *tmp;
	createStoreProjectsDir(); // cria a dir se necessário
	if (projectDir) {
		tmp = readdir(projectDir);
		while (tmp != NULL) {
			if (strcmp(tmp->d_name,".") && strcmp(tmp->d_name,"..")) {
				if (tmp->d_type == DT_DIR)
					list = insertElemTail(list,strdup(tmp->d_name));
			}
			tmp = readdir(projectDir);
		}
		closedir(projectDir);
	}
	free(path);
	return list;
}

static int openProject (const char *name) {
	Project *new = (Project *) malloc(sizeof(Project));
	FILE *fpUCEs, *fpStudents, *fpConfigs;
	char *pathUCEs;
	char *pathStudents;
	char *pathConfigs;
	pathUCEs = (char *) malloc(sizeof(char)*(strlen(PROJECT_PATH)+strlen(name)+10));
	pathStudents = (char *) malloc(sizeof(char)*(strlen(PROJECT_PATH)+strlen(name)+15)); 
	pathConfigs = (char *) malloc(sizeof(char)*(strlen(PROJECT_PATH)+strlen(name)+15));
	if (!new) logMessage("business","project.c","openProject","Memória Insuficiente",CRITICAL);
	new->name = strdup(name);
	sprintf(pathUCEs,"%s/%s/uces",PROJECT_PATH,name);
	sprintf(pathStudents,"%s/%s/students",PROJECT_PATH,name);
	sprintf(pathConfigs,"%s/%s/configs",PROJECT_PATH,name);
	fpUCEs = fopen(pathUCEs,"r");
	if (fpUCEs) {
		new->ucesMap = readUCEs(fpUCEs);
		fclose(fpUCEs);
	} else {
		logMessage("business","project.c","openProject","Impossivel abrir ficheiro de uces",ERRO);
	}
	fpStudents = fopen(pathStudents,"r");
	if (fpStudents) {
		new->students = readStudents(fpStudents);
		fclose(fpStudents);
	} else {
		logMessage("business","project.c","openProject","Impossivel abrir ficheiro de alunos",ERRO);
	}
	fpConfigs = fopen(pathConfigs,"r");
	if (fpConfigs) {
		new->configs = readConfiguration(fpConfigs);
		fclose(fpConfigs);
	} else {
		logMessage("business","project.c","openProject","Impossivel abrir ficheiro de configurações",ERRO);
	}
	free(pathUCEs); free(pathStudents); free(pathConfigs);
	actualProject = new;
	actualProjectSaved = 1;
	return 1;
}
static void freeProject (Project *p) {
	if (p) {
		if (p->name) free(p->name);
		freeArrayList(p->students);
		freeChainingHashMap(p->ucesMap);
		freeConfigurations(p->configs);
		freeLinkedList(p->configs);
		free(p);
	}
}
static int createProject (const char *name) {
	Project *new = (Project *) malloc(sizeof(Project));
	if (!new) logMessage("business","project.c","createProject","Memória Insuficiente",CRITICAL);
	new->name = strdup(name);
	new->students = newArrayList(STUDENT_LIST_SIZE);
	new->ucesMap = newChainingHashMap(UCE_HASHMAP_SIZE);
	new->ucesMap->hash = portugueseHash;
	new->ucesMap->eq = equalityString;
	new->configs = NULL;
	actualProject = new;
	actualProjectSaved = 0;
	return 1;
}

static int saveProject () {
	Project *p = actualProject;
	FILE *fpUCEs, *fpStudents, *fpConfigs;
	char *pathUCEs,*pathStudents, *pathConfigs;
	pathStudents = (char *) malloc(sizeof(char)*(strlen(PROJECT_PATH)+strlen(p->name)+15));
	pathUCEs = (char *) malloc(sizeof(char)*(strlen(PROJECT_PATH)+strlen(p->name)+10));
	pathConfigs = (char *) malloc(sizeof(char)*(strlen(PROJECT_PATH)+strlen(p->name)+10));
	sprintf(pathUCEs,"%s/%s/uces",PROJECT_PATH,p->name);
        sprintf(pathStudents,"%s/%s/students",PROJECT_PATH,p->name);
	sprintf(pathConfigs,"%s/%s/configs",PROJECT_PATH,p->name);
	createStoreProjectsDir(); // cria a directoria se for necessário!
	createProjectDir(p->name); // cria a directoria se for necessario!
	fpUCEs = fopen(pathUCEs,"w");
	if (fpUCEs) {
		writeUCEs(p->ucesMap,fpUCEs);
		fclose(fpUCEs);
	} else return 1;
	fpStudents = fopen(pathStudents,"w");
	if (fpStudents) {
		writeStudents(p->students,fpStudents);
		fclose(fpStudents);
	} else return 2;
	fpConfigs = fopen(pathConfigs,"w");
	if (fpConfigs) {
		writeConfiguration(p->configs,fpConfigs);
		fclose(fpConfigs);
	} else return 3;
	actualProjectSaved = 1;
	free(pathUCEs); free(pathConfigs); free(pathStudents);
	return 0;
}

void setProjectUnsaved () {
	actualProjectSaved = 0;
}

int hasProject () {
	return (actualProject != NULL);
}

CommandResult * command_listProjects (int argc, char *argv[]) {
	char *aux;
	LinkedList *tmp,*list = listProjects();
	if (list) printf("Projectos:\n\n");
	else printf("Não há projectos guardados\n");
	while (list) {
		aux = getData(list);
		printf(" %s\n",aux);
		free(aux);
		tmp = list;
		list = list->next;
		free(tmp);
	}
	return successCommandResult();
}

LinkedList * getConfigurationList () {
	if (!actualProject) return NULL;
	else return actualProject->configs;
}

CommandResult * command_saveProject (int argc, char *argv[]) {
	int aux,ind = -1;
	ArrayList *tmp;
	ChainingHashMap *map;
	if (!actualProject) {
		printf("Não existe nenhum projecto em memória\n");
		return successCommandResult();
	}
	if (argc == 2) {
		if (!strcmp(argv[1],"-f")) {
			ind = 1;
		} else if (!strcmp(argv[0],"-f")) {
			ind = 0;
		}
		if (ind != -1) {
			ind = 1 - ind;
			tmp = actualProject->students;
			map = actualProject->ucesMap;
			free(actualProject->name);
			createProject(argv[ind]);
			freeChainingHashMap(actualProject->ucesMap);
			freeArrayList(actualProject->students);
			actualProject->ucesMap = map;
			actualProject->students = tmp;
			aux = saveProject();
			if (!aux) {
				printf("Projecto gravado com sucesso!\n");
				return successCommandResult();
			}
			else {
				return newCommandResult("Impossivel abrir o ficheiro para escrita","Alguma aplicação poderá estar a usar o ficheiro",ARGUMENT);
			}
		} else {
			return newCommandResult("Parametros fornecidos inválidos","Argumentos possiveis: -f | <project name>",ARGUMENT);
		}
	} else if (argc == 1) {
		if (projectExists(argv[0])) {
			printf("O Projecto %s já existe, use -f para gravar por cima\n",argv[0]);
			return successCommandResult();
		}
		else {
			tmp = actualProject->students;
                       	map = actualProject->ucesMap;
                       	free(actualProject->name);
                       	createProject(argv[0]);
                       	freeChainingHashMap(actualProject->ucesMap);
			freeArrayList(actualProject->students);
                       	actualProject->ucesMap = map;
                       	actualProject->students = tmp;
                       	aux = saveProject();
                       	if (!aux) {
				printf("Projecto gravado com sucesso!\n");
				return successCommandResult();
			}
                       	else {
				return newCommandResult("Impossivel abrir o ficheiro para escrita","Alguma aplicação poderá estar a usar o ficheiro",ARGUMENT);
                    		}
		}
	} else if (argc == 0) {
		aux = saveProject();
		if (!aux) {
			printf("Projecto gravado com sucesso!\n");
			return successCommandResult();
		}
                else
			return newCommandResult("Impossivel abrir o ficheiro para escrita","Alguma aplicação poderá estar a usar o ficheiro",ARGUMENT);
	}
}

CommandResult * command_openProject (int argc, char *argv[]) {
	int ind = -1;
	Project *old = actualProject;
	if (argc == 2) {
		if (!strcmp(argv[0],"-f")) ind = 0;
		else if (!strcmp(argv[1],"-f")) ind = 1;
		if (ind != -1) {
			if (openProject(argv[(1-ind)])) {
				printf("Projecto carregado com sucesso!\n");
				if (old) freeProject(old);
				return successCommandResult();
			}
			else
				return newCommandResult("Erro ao carregar projecto","Projecto pode não existir ou estar a ser usado por outra aplicação",ARGUMENT);
		}
	}
	else if (argc == 1) {
		if (actualProject&&!actualProjectSaved) {
			printf("Tem um projecto aberto e não foi gravado. Use -f para forçar\n");
			return successCommandResult();
		} else {
			 if (openProject(argv[0])) {
				if (old) freeProject(old);
				printf("Projecto carregado com sucesso!\n");
                                return successCommandResult();
			}
                        else
                                return newCommandResult("Erro ao carregar projecto","Projecto pode não existir ou estar a ser usado por outra aplicação",ARGUMENT);
		}
	}
	else return newCommandResult("Poucos Argumentos fornecidos","<nome> (-f)",FEW_ARGUMENTS);
}
CommandResult * command_actualProject (int argc, char *argv[]) {
	if (!actualProject) {
		printf("Não existe projecto carregado\n");
	} else {
		printf("Project Info\n\nNome: %s\n",actualProject->name);
		if (actualProjectSaved) printf("Gravado: Sim\n");
		else printf("Gravado: Não\n"); 
	}
	return successCommandResult();
}
CommandResult * command_newProject (int argc, char *argv[]) {
	int ind = -1;
	Project *old = actualProject;
	if (argc == 2) {
                if (!strcmp(argv[0],"-f")) ind = 0;
                else if (!strcmp(argv[1],"-f")) ind = 1;
                if (ind != -1) {
                        if (openProject(argv[(1-ind)])) {
				if (old) freeProject(old);
				printf("Projecto criado com sucesso\n");
                                return successCommandResult();
			}
                        else
                                return newCommandResult("Erro ao carregar projecto","Projecto pode não existir ou estar a ser usado por outra aplicação",ARGUMENT);
                }
        }
	else if (argc == 1) {
		if (projectExists(argv[0])) {
			printf("Projecto %s já existe, use a flag -f para forçar\n",argv[0]);
			return successCommandResult();
		}
                else if (actualProject&&!actualProjectSaved) {
                        printf("Tem um projecto aberto e não foi gravado. Use -f para forçar\n");
                        return successCommandResult();
                } else {
                        if (createProject(argv[0])) {
				if (old) freeProject(old);
				printf("Projecto criado com sucesso!\n");
                                return successCommandResult();
			}
                        else
                                return newCommandResult("Erro ao carregar projecto","Projecto pode não existir ou estar a ser usado por outra aplicação",ARGUMENT);
                }
        }
        else return newCommandResult("Poucos Argumentos fornecidos","<nome> (-f)",FEW_ARGUMENTS);
}

CommandResult * command_setConfig (int argc, char *argv[]) {
	if (!actualProject) {
		printf("Não existe nenhum projecto aberto\n");
		return successCommandResult();
	}
	int tem = 0;
	Configuration *tmp;
	LinkedList *aux;
	if (argc < 2) {
		printf("Argumentos: '<nome>' '<valor>'\n");
	} else {
		aux = actualProject->configs;
		while (aux&&!tem) {
			tmp = getData(aux);
			if (!strcmp(tmp->name,argv[0])) tem = 1;
			aux = aux->next;
		}
		if (tem) {
			free(tmp->value); tmp->value = strdup(argv[1]);
			printf("Configuração alterada com sucesso!\n");
			setProjectUnsaved();
		} else {
			actualProject->configs = insertElemHead(actualProject->configs,newConfiguration(argv[0],argv[1]));
			printf("Configuração adicionada com sucesso!\n");				setProjectUnsaved();
		}
	}
	return successCommandResult();
}

CommandResult * command_showConfig (int argc, char *argv[]) {
	LinkedList *aux;
	Configuration *tmp;
	int tem = 0;
	if (!actualProject) {
		printf("Não existe nenhum projecto aberto\n");
	} else {
		aux = actualProject->configs;
                while (aux&&!tem) {
                        tmp = getData(aux);
                        if (!strcmp(tmp->name,argv[0])) tem = 1;
                        aux = aux->next;
                }
		if (tem) {
			printf("Valor: %s\n",tmp->value);
		} else {
			printf("Configuração não existe\n");
		}
	}
	return successCommandResult();
}

CommandResult * command_listConfigs (int argc, char *argv[]) {
	LinkedList *aux;
        Configuration *tmp;
	if (!actualProject) {
                printf("Não existe nenhum projecto aberto\n");
        } else {
                aux = actualProject->configs;
		if (aux) printf("Configurações:\n");
		else printf("Não existem configurações definidas\n");
                while (aux) {
                        tmp = getData(aux);
                        printf("%s = %s\n",tmp->name,tmp->value);
			aux = aux->next;
                }
        }
        return successCommandResult();
}

CommandResult * command_exit (int argc, char *argv[]) {
	if (argc >= 1) {
		if (!strcmp(argv[0],"-f")) {
			setDone();
		} else printf("Use a flag -f para forçar\n");
	} else {
		if (actualProject&&!actualProjectSaved) printf("O projecto actual não está gravado, use a flag -f para forçar\n");
		else setDone();
	}
	return successCommandResult();
}

char * getConfigurationValue (const char *name) {
	if (!actualProject) return NULL;
	Configuration *c;
	LinkedList *lst = actualProject->configs;
	while (lst) {
		c = getData(lst);
		if (!strcmp(c->name,name)) return c->value;
		lst = lst->next;
	}
	return NULL;
}
